skip to main content

developerWorks  >  Open source | Information Management | Java technology | Web development  >

Build an Ajax application using Google Web Toolkit, Apache Derby, and Eclipse, Part 3: Communication

Remote Procedure Calls (RPCs) and Google Web Toolkit
Traduction pour créer une application Ajax utilisant Google Web Toolkit, Apache Derby, et Eclipse
Procédure d'appels à d'instante (RPCs) et Google Web Toolkit

developerWorks

Niveau : Intermédiaire

Noel Rappin (noelrappin@gmail.com), Senior Software Engineer, Motorola, Inc.

13 Feb 2007

Dans les deux derniers articles de cette série, vous avez découvert comment créer une simple application Web en utilisant la trousse à outils de Web de Google (GWT) et la base de données relationnelle d'Apache Derby. La partie 1 focalisait sur l'emploie de GWT pour établir l'embout avant (front end) d'un système de livraison de pizza appelé Slicr. Dans la partie 2, vous avez obtenu une démonstration de la création d'une base de données relationnelle en utilisant Derby, et une démonstration de squelette ambulant d'un mécanisme pour convertir les enregistrements de base de données en objets de Java™. Maintenant ce devient intéressant ! En cet troisième article, découvrez comment obtenir que le client et le serveur parlant entre eux. Vous emploierez le framework Remote Procedure Call (RPC) dans GWT pour faire obtenir des données en dehors du serveur presque aussi simplement que si vous faisiez un appel de méthode en Java.

Le A dans Ajax

La différence principale entre un Javascript asynchrones + XML (Ajax)-activé sur une application Web et une application Web traditionnelle est le A de Ajax :asynchronous. Une application Ajax permet au navigateur de mettre à jour une partie spécifique de la page sans devoir régénérer complètement la page entière. Ce tour simple permet une expérience beaucoup plus interactive pour l'utilisateur, qu'une simple page Web, maintenant cela se comporte beaucoup plus comme une application locale d'ordinateur de bureau (desktop application).

Du point de vue du développeur, ce comportement asynchrone a deux composantes clés :

  • L'objet XMLHttpRequest est un objet de Javascript défini par le navigateur qui permet à une page Web d'envoyer des demandes de HTTP et de recevoir une réponse en tache de fond. À la différence d'une demande typique de page, l'appel n'interrompt pas l'interface utilisateur, et le navigateur ne fait pas une pause en attendant la réponse.
  • Un certain genre de rappel de service est exécuté après que la réponse soit complète. Le rappel de service emploie habituellement le modèle d'objet de document Javascript (DOM - JavaScript Document Object Model) pour manoeuvrer les éléments de la page basée sur les nouvelles données. Quelque chose de plus frais (rafraichit) se produit sur l'écran, et l'utilisateur est heureux.

From the developer's point of view, this asynchronous behavior has two key components:

  • The XMLHttpRequest object is a JavaScript object defined by the browser that enables a Web page to send HTTP requests and receive a response in a background thread. Unlike a typical page request, the call doesn't interrupt the user experience, and the browser doesn't pause while waiting for the response.
  • Some kind of callback is executed after the response is complete. That callback usually uses the JavaScript Document Object Model (DOM) to manipulate the elements of the page based on the new data. Something cool happens on screen, and the user is happy.

Ainsi le déroulement de base est : un appel au serveur, à une réponse en arrière fond, et aux mesures prises à la page basée sur la réponse. Encore, la chose importante à se rappeler est que toute la ceci a lieu dans les coulisses sans l'attente et la régénération liée à un changement typique de page dans un navigateur.

Il y a seulement un problème avec la méthode entière de XMLHttpRequest/DOM : C'est genre de douleur dans le cou. Chaque navigateur met en application les objets appropriés Javascript différemment. En conséquence, chaque framework Ajax digne de ce nom surcouche la création, les appels, et de la gestion de RPCs. GWT n'est pas une exception.

GWT contrôle RPCs avec une infrastructure de multiple-interface quelque peu réminiscente de la technologie de JavaBeans d'entreprise (EJB), mais -- remercions le -- beaucoup plus simplement. L'affaire est que vous définissez la liste d'appels à distance que votre système fait. GWT implémente d'aplomb l'arrière scène pour convertir les données pour qu'uelles aillent de nouveau vers le serveur, faisant un appel serveur, et convertissant les données retournées de nouveau en données client. Vous définissez alors quoi faire après que l'appel à distance soit complètement retourné. Ce n'est pas aussi facile que de faire un appel de méthode Java normale, mais ce n'est pas difficile.

Le changement que vous allez faire à l'application Slicr est de rechercher la liste initiale des garnitures depuis la base de données plutôt que de du codage en dur dans le code de client. Cet exemple évidemment simpliste marche à travers toutes les étapes requises pour faire un appel RPC. Pour exécuter cet exemple facilement, lancez le en mode hosted. En mode hosted, GWT simule automatiquement vos appels à distance. Vous obtiendrez un certain soutien en travaillant en mode hosted si vous employez un environnement intégré de développement (IDE), comme Eclipse ou d'IntelliJ IDEA.

Note : Dans l'article final de cette série, je montrerai comment lancer votre côté serveur dans un environnement normal de servlet.

Définir les services

La partie du travail dans votre appel au procédé GWT se produit dans deux classes. Du côté de serveur, vous définissez une sous-classe de RemoteServiceServlet. Dans cette classe, vous manoeuvrez les données serveur et renvoyez une valeur au client (ou lancez une exception, mais restons optimiste pour le moment). Du côté de client, vous définissez une classe qui implémente l'interface AsyncCallback. Dans cette classe, vous définissez ce que la page de client fait des données (ou de l'exception) quand le serveur est fait. En plus de ces deux classes, vous devez écrire un certain code copié (glue code) qui permet à GWT de lier ensemble la classe côté client et la classe côté serveur. OK, là c'est plutôt beaucoup de code copié. Le code copié se compose de deux interfaces différentes plus un quelque code côté client et un arrangement ou deux. Ne pas s'inquiéter; la majeure partie du code est des zones fixes (boilerplate), et si vous avez bien suivi cette étape tout ira bien.

Commencer du côté de serveur. Votre but ici est simplement de produire d'une liste de toutes les garnitures que vous avez placés dans la base de données à la fin de l'article précédent. Le serveur emploie le simple ObjectFactory de cet article (voir la liste 1) pour vous dépanner.


Liste 1. Implémentation du service des garnitures
	
public class ToppingServiceImpl extends RemoteServiceServlet 
		implements ToppingService {

	public static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";

	public static final String PROTOCOL = "jdbc:derby:slicr;";

	public List getAllToppings() {
		try {
			Class.forName(DRIVER).newInstance();
			Connection con = DriverManager.getConnection(PROTOCOL);
			Statement s = con.createStatement();
			ResultSet rs = s.executeQuery(
			        "SELECT * FROM toppings order by name");
			return ObjectFactory.convertToObjects(rs, Topping.class);
		} catch (Exception e) {
			e.printStackTrace();
			return new ArrayList();
		} finally {
			try {
				DriverManager.getConnection("jdbc:derby:;shutdown=true");
			} catch (SQLException ignore) {}
		}
	}

}

La première chose à notifier au sujet de ce code est qu'il n'y a aucune fantaisie. Les conditions pour un service à distance GWT sont simplement qu'il doit prolonger (extend) RemoteServiceServlet et implémenter une interface que vous créerez d'ici deux paragraphes.

Note : Il semble que documentation de GWT vous fait créer les interfaces d'abord. C'est très bien, mais n'importe vraiment pas. J'ai juste pensé qu'il serait plus clair avec le code concret d'abord.

Autrement, ce code a plus ou moins la même fonctionnalité que l'exemple de ToppingTestr de l'article précédent, revu pour un usage dans GWT. Vous faites un appel à la base de données Derby, employez l'usine d'objet pour créer des objets garnitures, et les renvoyez.

Glue code

Pour rendre votre nouveau service disponible à votre application du côté client, vous devez définir deux interfaces reliées. Le premier est l'interface ToppingService utilisée, mais non décrite, dans la liste 1. Il est vraiment simple, montré ici dans la liste 2.


Liste 2. Definir la première interface relative, ToppingService
public interface ToppingService extends RemoteService {
	public List getAllToppings();
}

Tout que vous avez fait ici, est de prendre la même signature vous avez employer pour la méthode dans la réelle classe concrète. La contrainte principale ici est que votre interface doit étendre com.google.gwt.user.client.rpc.RemoteService. Aussi, vos paramètres et valeur de retour doivent être des types que ce GWT peut sérialiser. Une liste de ces types est fournie dans la partie 2. Cependant, elle n'est pas assez pour avoir seulement une version de votre interface de service. Vous devez également définir une version asynchrone de l'interface, comme montré dans la liste 3.


Liste 3. Definir une version asynchrone de l'iinterface
public interface ToppingServiceAsync {
	public void getAllToppings(AsyncCallback callback);
}

La version asynchrone de l'interface de service est dérivée de la version primaire décrite ci-dessus. Les deux versions doivent être dans le même paquet, et ce paquet doit être évident à votre code de client de GWT. (J'ai utilisé com.ibm.examples.client). Le nom de classe dans la version asynchrone doit être le nom de votre interface originale avec la string Async supplémentaire en suffixe. Pour chaque méthode dans votre interface original, la version asynchrone doit avoir une méthode correspondante avec le type de retour changé en vide (void) et un paramètre additionnel de type AsyncCallback. Le code côté client emploie AsyncCallback pour agir sur la réponse de serveur.

Vous êtes parfaitement libre pour avoir autant de méthodes que vous voulez dans une interface à usage unique, tant que elles toutes ont des enfants de mêmes parents dans la version asynchrone et qu'elles implémentes toutes la même classe de service à distance. L'organisation des services est en grande partie une décision esthétique, bien que je suppose qu'il pourrait y avoir valeur pratique en ayant des méthodes de service capables partager des données communes du côté de serveur.

Une chose en plus, et alors votre code de serveur seront enregistrés. Ajouter la ligne suivante au fichier de Slicr.gwt.xml:

<servlet path="/toppings" class="com.ibm.examples.server.ToppingServiceImpl"/>

La ligne ci-dessus lie le nom entièrement qualifié de la classe de services à distance avec un nom qui est essentiellement l'URL pour ce service. Vous pouvez employer n'importe quel nom aussi longtemps que vous en souviendrez et que vous restez conforme. Techniquement, vous devez inclure seulement cette ligne dans le fichier .xml si vous allez lancez votre application en mode hosted. Comme vous verrez la fois prochaine, dans un déploiement Web, vous aurez besoin de quelque chose de semblable dans votre fichier de web.xml.

C'est beaucoup de choses à maintenir pour un appel simple -- l'interface, l'interface asynchrone, l'exécution réelle, et le fichie de module. En manquer un ou obtenir faire une faute mène aux erreurs quand vous essayez d'appeler réellement le service. En date de cette écriture, au moins un IDE (IntelliJ IDEA) fournit l'appui dans l'éditeur pour maintenir tous ces articles dans un état cohérent. Si vous employez Eclipse, le plug-in Googlipse fournit également un support semblable.

Créer un appel côté client

Le côté de serveur étant pris en compte, il est maintenant temps pour le client de faire un procédure d'appel. L'idée fondamentale ici est que vous appelez proprement le système de GWT le quel est service distant que vous appelez. Vous envoyez alors un objet AsyncCallback en dehors; par la suite, GWT vous le renvoi, et vous pouvez agir sur le résultat. La liste 2 expose le code pour l'installation et l'appel. Cette méthode est dans la classe de Slicr de la partie 1. Spécifiquement, l'appel à cette méthode remplace la ligne dans Slicr.onModuleLoad() qui ajoute le panneau des garnitures.


Liste 4. Setup et appel
	
public void callForToppings() {
	ToppingServiceAsync toppingService = 
		(ToppingServiceAsync) GWT.create(ToppingService.class);
	ServiceDefTarget target = (ServiceDefTarget) toppingService;
	String relativeUrl = GWT.getModuleBaseURL() + "toppings";
	target.setServiceEntryPoint(relativeUrl);
	toppingService.getAllToppings(new ToppingCallback());
}

Chaque ligne est une étape critique dans votre appel de GWT. Voici la liste de contrôle :

  1. Créer une instance de l'interface asynchrone. Typiquement, naturellement, vous ne pouvez pas créer une instance d'une interface, qui est là où l'appel de GWT.create() arrive. La classe GWT est un attrapeur-tout (catch-all) pour plusieurs utilités. Dans ce cas-ci, elle vous permet de créer un objet de procuration (proxy) qui met implémente une interface donnée. Notez que dans le déploiement, l'argument de cette méthode doit être littéraire plutôt qu'une variable. Les variables fonctionnent en mode accueilli (hosted), alors soyez prudents.
  2. L'objet de procuration retourné par GWT.create() implémente également une autre interface, ServiceDefTarget. Dans une ligne ou deux, vous allez avoir besoin d'une méthode de cette interface.
  3. Calculer l'URL au lequel vous devez envoyer le message. L'URL a deux composants:
    • L'URL de base pour le système, que vous obtenez en appelant GWT.getModuleBaseURL()
    • La même string que vous avez employé comme chemin en arrière (path back) quand vous avez ajouté le servlet au dossier de .xml
  4. Armé de l'URL complète, vous pouvez informer GWT que cet URL particulière est l'endroit à aller pour ce service particulier en appelant la méthode setServiceEntryPoint(). L'interface contient également la méthode relative d'acquéreur (getter). En ce moment, le service est entièrement initialisé pour l'utiliser dans la page cliente.
  5. Et finalement -- roulement de tambour, svp -- vous pouvez faire l'appel réel au service, juste comme le service le définit (excepté pour l'objet de ToppingCallback, qui est le sujet de la prochaine section).

Vous devez initialiser seulement le service (les quatre premières lignes de l'exemple) une fois dans la page de votre client. Après que le service soit créé et associé à son point d'entrée, vous pouvez réutiliser le même objet de service pour de nouveau, faire des appels multiples au serveur. Vous pouvez également créer quelques méthodes d'aide (helper) pour simplifier l'initialisation de zones fixes de l'objet serveur.

Les joies de l'asynchrone

Comme j'ai fait référence avant, une grande différence entre un appel par métode GWT RPC et un appel par méthode régulière est que vous ne savez pas quand ou si l'appel à distance va être accompli. Puisque les utilisateurs Web ne prennent normalement pas avec plaisir d'avoir des pages entières frisées pendant que vous régénérez la boîte choisie dans le coin, cotre programme GWT fait son appel à distance et continuent sur son joyeux chemin. Par la suite, le serveur répondra, et seulement à ce point que votre code de GWT fait la chose merveilleuse que vous avez projetée.

Souvent, maintenir les processus (threads) de fond dans ce genre d'environnement peut être un défi. Cependant, dans votre cas, GWT effectue la majeure partie du travail avec le mécanisme décrit ci-dessus et l'interface AsyncCallback que vous avez déjà vus. GWT simplifie efficacement les frais généraux de traiter les processus multiples.

L'interface AsyncCallback définit deux méthodes: OnSuccess(Object obj) et OnFailure(Throwable t). Vous devez définir une classe qui met implémente les deux. Vous créez une instance de la classe et le passez à votre méthode asynchrone de service quand vous faites votre appel à distance, comme vu dans la liste 4. Par la suite, la ressource de côté serveur est complète, et l'une des deux méthodes dans le code est appellée. L'argument à la méthode de succès est la valeur de retour de l'appel dans l'interface et l'implémentation. Dans votre cas, c'est la liste de garnitures. Vous la typerez (cast) au type prévu, et puis ferez quelque chose de utile avec les nouvelles données. Si tout va bien, GWT soutiendra bientot Java 1.5 générique, qui réduiraient le besoin de caster (typer). Si le code de côté serveur échoue, la méthode d'échec s'appelle avec comme argument l'exception générée. Encore, vous êtes libre pour faire ce que vous voulez pour répondre à l'exception.

Le code montré dans la liste 5 montre la classe de ToppingCallback visée dans la liste 4. Comme j'ai probablement mentionné avant, vous verrez fréquemment des classes AsyncCallback définies en tant que classes intérieures anonymes (anonymous inner classes). Je recommanderais d'éviter cela. Votre code sera beaucoup plus facile à lire et examiner si la classe a un nom joint et n'est pas placée bien au milieu d'un bloc de code entièrement différent.


Liste 5. La classe ToppingCallback
	
public class ToppingCallback implements AsyncCallback {

	public void onFailure(Throwable caught) {
	    GWT.log("Error ", caught);
		caught.printStackTrace();
	}

	public void onSuccess(Object result) {
		List allToppings = (List) result;
		VerticalPanel toppings = new VerticalPanel();
		toppings.add(new HTML("<h2>Toppings</h2>"));
		Grid topGrid = new Grid(allToppings.size() + 1, 3);
		topGrid.setText(0, 0, "Topping");
		topGrid.setText(0, 1, "Left");
		topGrid.setText(0, 2, "Right");
		for (int i = 0; i < allToppings.size(); i++) {
			Topping t = (Topping) allToppings.get(i);
			Button button = new Button(t.getName() );
			CheckBox leftCheckBox = new CheckBox();
			CheckBox rightCheckBox = new CheckBox();
			clearables.add(leftCheckBox);
			clearables.add(rightCheckBox);
			button.addClickListener(new ToppingButtonListener(leftCheckBox,
					rightCheckBox));
			topGrid.setWidget(i + 1, 0, button);	
			topGrid.setWidget(i + 1, 1, leftCheckBox);
			topGrid.setWidget(i + 1, 2, rightCheckBox);
		}
		toppings.add(topGrid);
		panel.add(toppings, DockPanel.EAST);
	}
	
}

Une exemple de la classe ToppingCallback est envoyé au moyen de l'interface asynchrone. GWT contrôle des choses dans les coulisses de sorte que d'abord, l'appel approprié soit fait au serveur, puis que le résultat de l'appel de serveur soit donné à l'objet de rappel de service, et finalement que la méthode appropriée de réponse soit appelé.

Dans ce cas-ci, il est plus facile décrire l'option d'échec. Si la méthode de côté serveur lève une exception pour n'importe quelle raison, la commande dans le rappel de service passe à la méthode onFailure(). L'argument à cette méthode est l'exception ou tout autre objet throwable que le côté serveur a renvoyé. Cet argument vous donne une chance de répondre avec élégance à l'échec, si possible. J'emploie la méthode GWT.log(), qui prend une string et un Throwable comme arguments. Cette méthode imprime un message de notation à la fenêtre shell du mode accueilli (hosted) de GWT. En tant que tels, cela fonctionne seulement en mode hosted; en mode Web, la méthode est ignorée. La méthode de GWT.log() est vraiment signifiée pour dépister (debuguer) pendant le développement. Vous voudrez probablement employer cette méthode pour mettre un message détaillé par défaut ou d'erreur dans la partie de l'écran que vous étiez sur le point pour régénérer, ou peut-être réorienter le navigateur entier à un écran d'erreurs.

Si vous avez lu le premier article de cette série, vous devriez noter presque immédiatement que le code onSuccess() est presque identique à la méthode buildToppingTypePanel() dans la version originale. Une des différences est insignifiante: Maintenant, vous ous faites une boucle au-dessus d'une liste plutôt que d'une rangée, qui cause les changements mineurs du commande de la boucle. J'ai également ajouté le prix de la garniture à l'affichage, parce que cette information est également disponible dans la base de données.

Plus profondément, la version précédente du code pouvait renvoyer le panneau de toppings au code appelant, et le code appelant pouvait alors ajouter le panneau toppings au panneau parent. Dans ce code, vous ne pouvez pas renvoyer une valeur, ainsi vous devez ajouter le récent panneau de création de garnitures au parent en tant qu'élément de cette méthode. Quand vous la lancez, vous verrez d'abord le paneau pizza; puis, après qu'un petit retard pour que GWT simule l'appel de serveur, le panneau des garnitures apparaîtra, tel que sur la figure 1.


Figure 1. Le nouveau panneau Topping
The New Topping panel

Même dans ce petit exemple, se déplacer à une structure asynchrone commence à avoir des effets sérieux sur la structure du code. Juste pour commmencer, l'ordre dans lequel les subpanels (sous panneaux) sont ajoutés au panneau principal est important déterminant la forme finale de chaque subpanel chez le parent. Car vous déplacez plus de l'initialisation aux appels de RPC, vous pouvez plus ne dépendre des subpanels étant créés dans l'ordre. Vous devez séparer la création initiale des panneaux en y insérant les widgets à l'intérieur. (Ceci devrait avoir l'avantage supplémentaire de la réduction clignotant quand les nouvelles données sont ajoutées.) Le but général est de maintenir le code à l'intérieur des rappels de service asynchrones aussi simple comme possible et comme sans relation directe entre eux comme possible. Avoir des processus fils (threads) qui dépendent d'un un autre augmente considérablement la complexité de votre code.

Share this...

digg Digg this story
del.icio.us Post to del.icio.us
Slashdot Slashdot it!

Ensuite?

Dans cet article, vous avez travaillé par le processus de création d'un simple RPC utilisant GWT. A ce niveau, vous avez maintenant une application légitime de GWT. Cependant, cette application fonctionne seulement en mode accueilli (hosted) sur votre machine de développement. Pour la montrer au grand public (Web), vous devez déplacer votre application de Web vers le mode Web et la déployer dans un environnement de servlet. La finalité de cette série d'article couvrira le déploiement.



Resources

Article traduit en Français

Original English article
Learn

Get products and technologies
  • Download the Google Web Toolkit.

  • Download Eclipse, the open source, freely available, and extensible IDE.

  • Innovate your next open source development project with IBM trial software, available for download or on DVD.


Discuss


A propos de l'auteur

Noel Rappin, a Ph.D. from the Graphics, Visualization, and Usability Center at the Georgia Institute of Technology, is a senior software engineer at Motorola. He is also the coauthor of wxPython in Action and Jython Essentials. You can check out Noel's blog at www.10printhello.blogspot.com.